<?php
/*
# $Id: Core.php 12 2006-11-21 12:51:48Z James $
*/

/*
# @class Core
*/
class Core {

	/*
	# @method void configure( string $configPath )
	# $configPath	= Abs. folder path to the application's config folder
	#
	# This method prepares the Buan environment by reading and acting on a set
	# of configuration scripts found in $configPath.
	#
	# $configPath should contain the following script:
	# app.php		= Set config variables, db connections and Model relations
	# bootstrap.php	= An optional script that performs your custom routines
	#
	# The following tasks are executed in order:
	# 1. "app.php" is executed.
	# 2. Any extensions defined in "app.extensions" are initialised.
	# 3. "bootstrap.php" is executed.
	*/
	static public function configure($configPath) {

		// Include 'app' configuration file
		if(include($configPath.'/app.php')) {

			// Check for existence of some required settings and set defaults or halt
			$app = Config::get('app');
			if(!isset($app['docRoot'])) {
				SystemLog::add("You have not defined your application's document root in {$configPath}/app.php.", SystemLog::FATAL);
				Core::halt();
			}
			if(!isset($app['command']['urlPrefix'])) {
				$app['command']['urlPrefix'] = 'do';
			}
			if(!isset($app['command']['parameter']) || $app['command']['parameter']==='') {
				$app['command']['parameter'] = 'do';
			}
			if(!isset($app['command']['default'])) {
				$app['command']['default'] = '';
			}
			if(!isset($app['dir']['ignored'])) {
				$app['dir']['ignored'] = array('.', '..', '.svn', 'cvs');
			}
			if(!isset($app['domain'])) {
				$app['domain'] = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost';
			}
			if(!isset($app['extensions'])) {
				$app['extensions'] = array();
			}
			if(!isset($app['password'])) {
				$app['password'] = rand(1000000, 9999999);
			}
			if(!isset($app['urlRoot'])) {
				$app['urlRoot'] = '';
			}
			else if($app['urlRoot']!=='') {
				$app['urlRoot'] = preg_replace("/^\/*/", "/", preg_replace("/\/+$/", "", $app['urlRoot']));
			}

			// Add some directories to the auto-loader's list of search paths.
			// Need to consider what class instances may be stored in session because once a session is restarted
			// those classes need to be immediately available.
			if(isset($app['dir']['models'])) {
				BuanAutoLoader::addClassPath($app['dir']['models']);
			}
			if(isset($app['dir']['modelManagers'])) {
				BuanAutoLoader::addClassPath($app['dir']['modelManagers']);
			}

			// Reassign filtered settings to Config
			Config::set('app', $app);

			// Ensure that any encoded forward slashes in the command URL remain encoded
			if(isset($_SERVER['QUERY_STRING']) && preg_match("/".$app['command']['parameter']."=([^&]*\%2F[^&]*)/i", $_SERVER['QUERY_STRING'], $m)>0) {
				$_REQUEST[$app['command']['parameter']] = $_GET[$app['command']['parameter']] = $m[1];
			}

			// Store the requested command
			$cp = Config::get('app.command.parameter');
			Config::set('app.command.requested', isset($_REQUEST[$cp]) ? $_REQUEST[$cp] : '');
		}
		else {
			// Log and halt
			SystemLog::add("Your application's configuration file is missing (looking for $configPath/app.php)", SystemLog::FATAL);
			Core::halt();
		}

		// Store the given application configuration path in the global Config
		Config::set('app.dir.config', $configPath);

		// Initialise all extensions used by the application
		$extensions = Config::get('app.extensions');
		if(!empty($extensions)) {
			foreach($extensions as $extName) {
				$ext = Extension::getExtensionByName($extName);

				// Check the extension is actually available
				if($ext===NULL) {
					SystemLog::add(array('A required extension (%s) is not available to this application.', $extName), SystemLog::WARNING);
					continue;
				}

				// Initialise the extension
				else if(!$ext->init()) {
					SystemLog::add(array('An extension (%s) failed to initialise correctly.', $extName), SystemLog::WARNING);
					continue;
				}
			}
		}

		// Include the application's main 'bootstrap' script
		if(is_file("{$configPath}/bootstrap.php")) {
			include_once("{$configPath}/bootstrap.php");
		}
	}

	/*
	# @method bool boot( [string $urlPath] )
	# $urlPath	= The path portion of the URI
	#
	# Performs the following tasks:
	# 1. Generates a UrlCommand instance from the given $urlPath
	# 2. Executes the UrlCommand and attaches the resulting View to the GlobalView
	# 3. Renders the GlobalView to output.
	*/
	static public function boot($urlPath=NULL) {

		// Vars
		if($urlPath===NULL || $urlPath=='') {
			$cr = Config::get('app.command.requested');
			$urlPath = $cr=='' ? Config::get('app.command.default') : $cr;
		}

		// Create and execute the command then attach the resulting View to the
		// GlobalView's "action" slot.
		$command = UrlCommand::create($urlPath);
		$commandView = $command->execute();
		$globalView = View::getGlobalView();
		$globalView->attachViewToSlot($commandView, 'action');

		// Render the GlobalView
		echo $globalView->render();

		// Result
		return TRUE;
	}

	/*
	# @method void halt()
	#
	# Stops the system in it tracks and renders whatever Views have been added to the queue.
	#
	# TODO: If any calls to Core::halt() occur within the rendering code or templates then recursion will appear.
	# How do we get around that? Set a static var to ensure this is called only once?
	*/
	static public function halt() {

		// Render and display the GlobalView (if not already done so)
		$globalView = View::getGlobalView();
		echo $globalView->render();

		// Exit
		self::shutdown();
	}

	/*
	# @method shutdown()
	#
	# Deinitialises the application, generally cleaning up.
	*/
	static public function shutdown() {

		// Close all DB connections

		// Close session

		// Run an application's custom "shutdown" script

		// Stop and exit the script execution
		exit();
	}

	/*
	# @method bool hasAdminAccess()
	#
	# Determines if the user has logged in using the application password.
	# If not then a common reaction would be to build the application login interface using:
	#	Core::getLoginView()
	*/
	static public function hasAdminAccess() {

		// Start session if not already done so
		$doCloseSession = FALSE;
		if(session_id()=="") {
			session_start();
			$doCloseSession = TRUE;
		}

		// Find appropriate flag in session
		if(isset($_SESSION['buan']['hasAdminAccess']) && $_SESSION['buan']['hasAdminAccess']==TRUE) {
			$hasAdminAccess = TRUE;
		}
		else {
			$hasAdminAccess = FALSE;
		}

		// Close session, if it was opened in this method
		if($doCloseSession) {
			session_write_close();
		}

		// Result
		return $hasAdminAccess;
	}

	/*
	# @method void setAdminAccess( [bool $state=FALSE] )
	# $state	The state to set the access flag to
	#
	# Sets whether or not the user has admin access to the core of the application.
	*/
	static public function setAdminAccess($state=FALSE) {

		// Start session if not already done so
		$doCloseSession = FALSE;
		if(session_id()=="") {
			session_start();
			$doCloseSession = TRUE;
		}

		// Set flag
		$_SESSION['buan']['hasAdminAccess'] = $state;

		// Close session, if it was opened in this method
		if($doCloseSession) {
			session_write_close();
		}
	}

	/*
	# @method View getLoginView( [string $refererUrl=NULL] )
	# $refererUrl	= Once the user successfully logs in they will be redirected to this URL
	#
	# Returns the application login View (/core/app_login) so the user can potentially gain core admin access.
	*/
	static public function getLoginView($refererUrl=NULL) {

		// Create the view
		$command = UrlCommand::create('/core/app_login');
		$view = $command->execute();
		$view->refererUrl = $refererUrl;

		// Result
		return $view;
	}
}
?>